package com.zebra.gateway.processor;

import com.zebra.common.ZebraException;
import com.zebra.gateway.GwConfigInfoManager;
import com.zebra.gateway.protocol.GatewayCommand;
import com.zebra.gateway.protocol.GatewaySendMessageUtil;
import com.zebra.gateway.session.Session;
import com.zebra.gateway.session.SessionManager;
import com.zebra.remoting.NotifyProxy;
import com.zebra.remoting.config.*;
import com.zebra.remoting.connection.Connection;
import com.zebra.remoting.protocol.ProcessorType;
import com.zebra.remoting.util.RemotingUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

public abstract class AbstractGatewayProcessor implements GatewayProcessor {
    private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractGatewayProcessor.class);

    @Override
    public void process(ChannelHandlerContext ctx, GatewayCommand msg, Executor defaultExecutor) throws Exception {
            try {
                doProcess(ctx, msg);
            } catch (ZebraException e) {
                logger.error("Handler process zebraException {} {} {}", e.getMessage(), msg.toString(), RemotingUtil.channelToString(ctx.channel()));
                doZebraException(ctx, msg, e.getExceptionCode());
            } catch (Exception e) {
                logger.error("Handler process exception {} {} {}", e.getMessage(), msg.toString(), RemotingUtil.channelToString(ctx.channel()));
                GatewaySendMessageUtil.sendStateMessage(ctx, msg, StateCode.SysException.code());
            }
    }

    private void doZebraException(ChannelHandlerContext ctx, GatewayCommand msg, int exceptionCode){
        ProcessorType processorType = msg.getProcessorType();
        if (ProcessorType.L_LOGIN == processorType || ProcessorType.L_RECON == processorType || ProcessorType.G_RECON == processorType){
            GatewaySendMessageUtil.sendStateMessage(ctx, msg, exceptionCode);
            return;
        }
        Session session = SessionManager.getInstance().getSession(ctx.channel());
        if (null == session){
            logger.error("Handler process zebraException session is null ecode:{} {} {}", exceptionCode, msg.toString(), RemotingUtil.channelToString(ctx.channel()));
            GatewaySendMessageUtil.sendStateMessage(ctx, msg, exceptionCode);
            return;
        }
        GatewaySendMessageUtil.sendStateMessage(ctx, msg, exceptionCode);
        if (StateCode.LoginUn.code() == exceptionCode){
            GatewayProcessorUtil.sendTmsLoginUn(session); //发消息 下线消息
        }
    }

    public abstract void doProcess(ChannelHandlerContext ctx, GatewayCommand msg) throws  Exception;



    public Session getSessionByServer(ChannelHandlerContext ctx, GatewayCommand command){
        Session session = SessionManager.getInstance().getSession(ctx.channel()); //必须通过channel 去拿 不然存在安全问题，脱离channel 任何地方都可以操作
        if (null == session){
            logger.error("GATEWAY SESSION is null {} {}",  RemotingUtil.channelToString(ctx.channel()), command.toString());
            GatewaySendMessageUtil.sendStateMessage(ctx, command, StateCode.LoginUn.code()); // 如果 下发 可能误杀
            return null;
        }

        if (command.getToken() != session.getToken() || session.isWeakClose()){
            logger.error("GATEWAY SESSION not disagree {} {}; stk {}",  RemotingUtil.channelToString(ctx.channel()), command.toString(), session.getToken());
            GatewaySendMessageUtil.sendStateMessage(ctx, command, StateCode.LoginUn.code());
            GatewayProcessorUtil.sendTmsLoginUn(session); //发消息
            return null;
        }

        command.setUserId(session.getUserId());
        return session;
    }

    /**
     * 获取注册中心服务 Connection
     * @return
     * @throws ZebraException
     */
    private Connection getRegisterConnection()throws ZebraException{
        Connection registerConn = GwConfigInfoManager.getInstance().getRAConnection();
        if (null == registerConn){
            logger.error("Gateway   Register is null; gw {}", GwConfigInfoManager.getInstance().getGwServerInfo().toString()); //如果没有注册中心
            throw new ZebraException(StateCode.GatewayDowntime.code());   //TODO 需要启动重大事故策略
        }
        return registerConn;
    }

    /**
     * 获取 TMS 服务器 Connection
     * @return
     * @throws ZebraException
     */
    public Connection getTmsConnection(long userId, long token, int cmd, short gameId, ProcessorType ptype)throws Exception{
        Connection registerConnection = getRegisterConnection();  //注册中心 connection

        GatewayCommand ASKTMS = GatewayCommand.builder()
                .userId(userId)
                .token(token)
                .gameId(gameId)
                .cmd(BasisCommandCode.ASKTMS.cmd())
                .message(AskTmsRequestMessage.builder().cmd(cmd).processorType(ptype.value()).build())
                .build();
        ASKTMS.messageEncoder();
        GatewayCommand command = (GatewayCommand) NotifyProxy.notifyProxy.syncMsg(registerConnection.getChannel(), ASKTMS); //通过注册中心获取TMS serveId
        if (command == null){
            logger.error("Gateway AskTMS is null; {} Ra {}, gw {}", ASKTMS.toString(), registerConnection.toString(), GwConfigInfoManager.getInstance().getGwServerInfo().toString());
            throw new ZebraException(StateCode.GatewayDowntime.code());
        }
        if (StateCode.Success.code() != command.getState()){
            command.release();
            throw new ZebraException(command.getState());
        }

        // 应该进入的TMS serverId
        AskTmsResponseMessage askTmsResponseMessage = new AskTmsResponseMessage().read(command.getMsgBuf());
        int serverId = askTmsResponseMessage.getServerId();
        Connection tmsConnection = GwConfigInfoManager.getInstance().getTMSConnection(serverId, userId);
        if (null == tmsConnection){
            //如果发现serverId 链接没有 则需要向注册中心询问 TMS 信息 进行注册
            logger.error("Gateway AskTMS Connection is null serverId {}", serverId);
            GatewayProcessorUtil.doRaTmsBySid(serverId, registerConnection);
            TimeUnit.MILLISECONDS.sleep(600);
            tmsConnection = GwConfigInfoManager.getInstance().getTMSConnection(serverId, userId);
        }
        if (null == tmsConnection){
            logger.error("Gateway AskTMS Connection is null by serverId {} userId {} ", serverId, userId);
            StateCode stateCode = gameId == 0 ? StateCode.LobbyServerDowntime : StateCode.GameServerDowntime;
            throw new ZebraException(stateCode.code());
        }
        return tmsConnection;
    }


    public void sendSWITCHTMS(Connection tmsConnection,  Session session, ProcessorType processorType){
        if (ProcessorType.GM == processorType){
            GatewayCommand gatewayCommand = GatewayCommand.builder()
                    .cmd(BasisCommandCode.SWITCHTMS.cmd())
                    .token(session.getToken())
                    .userId(session.getUserId())
                    .message(new EmptyMessage())
                    .build();
            GatewaySendMessageUtil.sendMessage(tmsConnection.getChannel(), gatewayCommand);
        }
    }
}
